/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						   dialog_view_world.cpp

	$Header: /game/dialog_view_world.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "dialog_view_world.h"

#include <algorithm>
#include <assert.h>
#include <string>

#include "abstract_tile.h"
#include "adaptor_handler.h"
#include "adv_object_icon_type.h"
#include "adventure_map.h"
#include "adventure_object.h"
#include "adventure_tile_size.h"
#include "bitmap_group.h"
#include "bitmap_group_cache.h"
#include "bitmap_layer.h"
#include "bitmap_layer_cache.h"
#include "bitmap_layer_cache_window.h"
#include "bound_handler.h"
#include "button.h"
#include "button_cache.h"
#include "button_group.h"
#include "cached_ptr.h"
#include "convert_16_bit.h"
#include "enum_operations.h"
#include "external_string.h"
#include "footprint.h"
#include "handler.h"
#include "interface_help.h"
#include "memory_bitmap.h"
#include "mini_map_renderer.h"
#include "mouse_event.h"
#include "owned_ptr.h"
#include "paint_surface.h"
#include "platform.h"
#include "player.h"
#include "player_color.h"
#include "scaled_adventure_map_drawer.h"
#include "screen_point.h"
#include "shared_ptr.h"
#include "standard_fonts.h"
#include "text_window.h"
#include "toggle_button.h"

extern t_button_cache g_ok_button;
extern t_button_cache g_surface_button;
extern t_button_cache g_underground_button;

// --------------------------------------------------------------------------
// Unnamed namespace
// --------------------------------------------------------------------------

namespace
{

	t_bitmap_group_cache g_layout_cache( "dialog.view_world" );
	t_bitmap_group_cache g_zoom_button_cache( "button.magnify" );

	// --------------------------------------------------------------------------
	// t_icon_layer_id enum
	// --------------------------------------------------------------------------

	enum t_icon_layer_id
	{
		k_icon_layer_army,
		k_icon_layer_artifact,
		k_icon_layer_blue_flag,
		k_icon_layer_crystal,
		k_icon_layer_dwelling,
		k_icon_layer_gems,
		k_icon_layer_gold,
		k_icon_layer_green_flag,
		k_icon_layer_grey_flag,
		k_icon_layer_mercury,
		k_icon_layer_orange_flag,
		k_icon_layer_ore,
		k_icon_layer_purple_flag,
		k_icon_layer_random,
		k_icon_layer_red_flag,
		k_icon_layer_subterranean_gate,
		k_icon_layer_sulfur,
		k_icon_layer_teal_flag,
		k_icon_layer_teleporter,
		k_icon_layer_town,
		k_icon_layer_wood,

		k_icon_layer_count,
	};

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_abstract_cache< t_bitmap_layer > & get_cache( t_icon_layer_id layer_id )
	{
		assert( layer_id >= 0 && layer_id < k_icon_layer_count );
		
		static t_bitmap_group_cache icons_cache( "icons.view_world" );

		static t_bitmap_layer_cache layer_caches[ k_icon_layer_count ] =
		{
			t_bitmap_layer_cache( icons_cache, "army" ),
			t_bitmap_layer_cache( icons_cache, "artifacts" ),
			t_bitmap_layer_cache( icons_cache, "blue" ),
			t_bitmap_layer_cache( icons_cache, "crystal" ),
			t_bitmap_layer_cache( icons_cache, "dwellings" ),
			t_bitmap_layer_cache( icons_cache, "gems" ),
			t_bitmap_layer_cache( icons_cache, "gold" ),
			t_bitmap_layer_cache( icons_cache, "green" ),
			t_bitmap_layer_cache( icons_cache, "grey" ),
			t_bitmap_layer_cache( icons_cache, "mercury" ),
			t_bitmap_layer_cache( icons_cache, "orange" ),
			t_bitmap_layer_cache( icons_cache, "ore" ),
			t_bitmap_layer_cache( icons_cache, "purple" ),
			t_bitmap_layer_cache( icons_cache, "random" ),
			t_bitmap_layer_cache( icons_cache, "red" ),
			t_bitmap_layer_cache( icons_cache, "subterranean_gates" ),
			t_bitmap_layer_cache( icons_cache, "sulfur" ),
			t_bitmap_layer_cache( icons_cache, "teal" ),
			t_bitmap_layer_cache( icons_cache, "teleporters" ),
			t_bitmap_layer_cache( icons_cache, "town" ),
			t_bitmap_layer_cache( icons_cache, "wood" ),
		};

		return layer_caches[ layer_id ];
	}

	// --------------------------------------------------------------------------
	// t_simple_icon_window class
	// --------------------------------------------------------------------------

	class t_simple_icon_window : public t_bitmap_layer_cache_window
	{
	public:
		// Constructor
		t_simple_icon_window(
			t_screen_point const &	point,
			t_window *				parent_ptr,
			t_icon_layer_id			layer_id,
			t_player_color			player_color = k_player_gray );
	};

	// --------------------------------------------------------------------------
	// t_simple_icon_window members
	// --------------------------------------------------------------------------

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_simple_icon_window::t_simple_icon_window(
		t_screen_point const &	point,
		t_window *				parent_ptr,
		t_icon_layer_id			layer_id,
		t_player_color			player_color )
		:	t_bitmap_layer_cache_window( get_cache( layer_id ).get(), point, parent_ptr )
	{
	}

	// --------------------------------------------------------------------------
	// t_flagged_icon_window class
	// --------------------------------------------------------------------------

	class t_flagged_icon_window : public t_window
	{
	public:
		// Constructor
		t_flagged_icon_window(
			t_screen_point const &	point,
			t_window *				parent_ptr,
			t_icon_layer_id			layer_id,
			t_player_color			player_color = k_player_gray );

	private:
		// Static member functions
		static t_icon_layer_id	get_flag_layer_id( t_player_color color );
	};

	// --------------------------------------------------------------------------
	// t_flagged_icon_window members
	// --------------------------------------------------------------------------

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_flagged_icon_window::t_flagged_icon_window(
		t_screen_point const &	point,
		t_window *				parent_ptr,
		t_icon_layer_id			layer_id,
		t_player_color			player_color )
		:	t_window( k_completely_transparent, parent_ptr )
	{
		t_icon_layer_id flag_layer_id = get_flag_layer_id( player_color );
		t_bitmap_layer_ptr flag_layer_ptr = get_cache( flag_layer_id ).get();
		t_bitmap_layer_ptr icon_layer_ptr = get_cache( layer_id ).get();

		t_screen_rect rect = get_extent( flag_layer_ptr->get_rect(), icon_layer_ptr->get_rect() );
		init( rect + point );

		new t_simple_icon_window( t_screen_point( 0, 0 ), this, flag_layer_id );
		new t_simple_icon_window( t_screen_point( 0, 0 ), this, layer_id );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_icon_layer_id t_flagged_icon_window::get_flag_layer_id(
		t_player_color	color )
	{
		assert( color >= 0 && color < k_player_color_count );

		static t_icon_layer_id const k_layer_ids[ k_player_color_count ] =
		{
			k_icon_layer_red_flag,
			k_icon_layer_blue_flag,
			k_icon_layer_green_flag,
			k_icon_layer_orange_flag,
			k_icon_layer_purple_flag,
			k_icon_layer_teal_flag,
			k_icon_layer_grey_flag,
		};

		return k_layer_ids[ color ];
	}

	// --------------------------------------------------------------------------
	// t_abstract_icon_window_factory class
	// --------------------------------------------------------------------------

	class t_abstract_icon_window_factory
	{
	public:
		// Member functions
		virtual	t_window_ptr	create(
									t_screen_point const &	point,
									t_window *				parent_ptr,
									t_player_color			player_color = k_player_gray ) const = 0;

	protected:
		// Destructor
		~t_abstract_icon_window_factory() {}
	};

	// --------------------------------------------------------------------------
	// t_icon_window_factory class template
	// --------------------------------------------------------------------------

	template < typename t_icon_window >
	class t_icon_window_factory : public t_abstract_icon_window_factory
	{
	public:
		// Constructor
		explicit t_icon_window_factory( t_icon_layer_id layer_id );

		// Member functions
		virtual t_window_ptr	create(
									t_screen_point const &	point,
									t_window *				parent_ptr,
									t_player_color			player_color = k_player_gray ) const;

	private:
		// Data members
		t_icon_layer_id	m_layer_id;
	};

	// --------------------------------------------------------------------------
	// t_icon_window_factory members
	// --------------------------------------------------------------------------

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	template < typename t_icon_window >
	t_icon_window_factory< t_icon_window >::t_icon_window_factory(
		t_icon_layer_id	layer_id )
		:	m_layer_id( layer_id )
	{
		assert( layer_id >= 0 && layer_id < k_icon_layer_count );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	template < typename t_icon_window >
	t_window_ptr t_icon_window_factory< t_icon_window >::create(
		t_screen_point const &	point,
		t_window *				parent_ptr,
		t_player_color			player_color ) const
	{
		return t_window_ptr( new t_icon_window( point, parent_ptr, m_layer_id, player_color ) );
	}

	// --------------------------------------------------------------------------
	// Non-member functions
	// --------------------------------------------------------------------------

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	typedef t_abstract_icon_window_factory const * t_icon_window_factory_ptr_table[ k_adv_object_icon_type_count ];
	t_icon_window_factory_ptr_table const & get_icon_window_factory_ptr_table()
	{
		typedef t_icon_window_factory< t_simple_icon_window > t_simple_factory;
		typedef t_icon_window_factory< t_flagged_icon_window > t_flagged_factory;

		static t_flagged_factory const	k_alchemists_lab_factory( k_icon_layer_mercury );
		static t_flagged_factory const	k_army_factory( k_icon_layer_army );
		static t_simple_factory const	k_artifact_factory( k_icon_layer_artifact );
		static t_flagged_factory const	k_creature_dwelling_factory( k_icon_layer_dwelling );
		static t_simple_factory const	k_crystal_factory( k_icon_layer_crystal );
		static t_flagged_factory const	k_crystal_mine_factory( k_icon_layer_crystal );
		static t_flagged_factory const	k_gem_mine_factory( k_icon_layer_gems );
		static t_simple_factory const	k_gems_factory( k_icon_layer_gems );
		static t_simple_factory const	k_gold_factory( k_icon_layer_gold );
		static t_flagged_factory const	k_gold_mine_factory( k_icon_layer_gold );
		static t_simple_factory const	k_mercury_factory( k_icon_layer_mercury );
		static t_simple_factory const	k_ore_factory( k_icon_layer_ore );
		static t_flagged_factory const	k_ore_pit_factory( k_icon_layer_ore );
		static t_flagged_factory const	k_sawmill_factory( k_icon_layer_wood );
		static t_simple_factory const	k_subterranean_gate_factory( k_icon_layer_subterranean_gate );
		static t_simple_factory const	k_sulfur_factory( k_icon_layer_sulfur );
		static t_flagged_factory const	k_sulfur_mine_factory( k_icon_layer_sulfur );
		static t_simple_factory const	k_teleporter_factory( k_icon_layer_teleporter );
		static t_flagged_factory const	k_town_factory( k_icon_layer_town );
		static t_simple_factory const	k_wood_factory( k_icon_layer_wood );

		static t_icon_window_factory_ptr_table const k_factory_table =
		{
			&k_alchemists_lab_factory,
			&k_army_factory,
			&k_artifact_factory,
			&k_creature_dwelling_factory,
			&k_crystal_factory,
			&k_crystal_mine_factory,
			&k_gem_mine_factory,
			&k_gems_factory,
			&k_gold_factory,
			&k_gold_mine_factory,
			&k_mercury_factory,
			&k_ore_factory,
			&k_ore_pit_factory,
			&k_sawmill_factory,
			&k_subterranean_gate_factory,
			&k_sulfur_factory,
			&k_sulfur_mine_factory,
			&k_teleporter_factory,
			&k_town_factory,
			&k_wood_factory,
		};

		return k_factory_table;
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_window_ptr create_icon_window(
		t_screen_point const &	point,
		t_window *				parent_ptr,
		t_adv_object_icon_type	icon_type,
		t_player_color			player_color = k_player_gray )
	{
		assert( icon_type >= 0 && icon_type < k_adv_object_icon_type_count );

		static t_icon_window_factory_ptr_table const & k_factory_ptr_table =
			get_icon_window_factory_ptr_table();

		return k_factory_ptr_table[ icon_type ]->create( point, parent_ptr, player_color );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_screen_rect get_view_rect( t_adventure_map const & map )
	{
		t_screen_point view_size = map.get_view_size();
		return t_screen_rect( 0, -k_max_tile_vertex_height, view_size.x, view_size.y );
	}

	// --------------------------------------------------------------------------
	// t_compare_depth function object
	// --------------------------------------------------------------------------

	class t_compare_depth
	{
	public:
		// Constructor
		explicit t_compare_depth( t_adventure_map const & map );

		// Operator
		bool	operator()(
					t_adventure_map::t_object_id	first,
					t_adventure_map::t_object_id	second ) const;

	private:
		// Data members
		t_adventure_map const &	m_map;

		// Member functions
		int	get_depth( t_adventure_map::t_object_id id ) const;
	};

	// --------------------------------------------------------------------------
	// t_compare_depth function object
	// --------------------------------------------------------------------------

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	inline t_compare_depth::t_compare_depth( t_adventure_map const & map )
		:	m_map( map )
	{
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	inline bool t_compare_depth::operator()(
		t_adventure_map::t_object_id	first,
		t_adventure_map::t_object_id	second ) const
	{
		return get_depth( first ) < get_depth( second );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	int t_compare_depth::get_depth( t_adventure_map::t_object_id id ) const
	{
		t_adventure_object const & object = m_map.get_adv_object( id );
		t_map_point_2d const & object_pos = object.get_position();
		t_map_point_2d const & footprint_size = object.get_footprint().get_size();
		return ( object_pos.row + object_pos.column ) * 2 + footprint_size.row + footprint_size.column;
	}

	// --------------------------------------------------------------------------
	// t_map_pane class
	// --------------------------------------------------------------------------

	class t_map_pane : public t_window
	{
	public:
		// Types
		enum t_zoom_level
		{
			k_zoom_most,
			k_zoom_medium,
			k_zoom_least,

			k_zoom_level_count,
		};

		typedef t_handler_1< t_screen_point const & > t_move_viewport_handler;

		// Static member functions
		static t_screen_point	get_viewport_size(
									t_screen_point const &	window_size,
									t_zoom_level			zoom_level );

		// Constructor
		t_map_pane(
			t_screen_rect const &	rect,
			t_window *				parent_ptr,
			t_adventure_map const &	map,
			t_screen_point const &	viewport_pos,
			int						view_level,
			int						team_view,
			t_move_viewport_handler	move_viewport_handler );

		// Member functions
		t_adventure_map const &	get_adventure_map() const;
		t_screen_rect			get_map_view_rect() const;
		int						get_view_level() const;
		t_screen_point			get_viewport_pos() const;
		t_screen_point			get_viewport_size() const;
		t_screen_point			get_viewport_size( t_zoom_level zoom_level ) const;
		t_zoom_level			get_zoom_level() const;
		virtual void			left_button_down( t_mouse_event const & event );
		virtual void			left_button_up( t_mouse_event const & event );
		virtual void			mouse_move( t_mouse_event const & event );
		void					move_viewport( t_screen_point const & new_viewport_pos );
		virtual void			paint( t_paint_surface & paint_surface );
		void					set_view_level( int new_view_level );
		void					set_zoom_level(
									t_zoom_level			new_zoom_level,
									t_screen_point const &	new_viewport_pos );

	private:
		// Types
		struct t_zoom_level_properties
		{
			t_scaled_adventure_map_drawer::t_scale	scale;
		};

		struct t_icon_info
		{
			t_window_ptr	window_ptr;
			t_screen_point	screen_point;
			int				level;
		};

		typedef std::vector< t_icon_info > t_icon_info_list;

		// Static data
		static t_zoom_level_properties const	k_zoom_level_properties_array[ k_zoom_level_count ];

		// Data members
		t_screen_point						m_buffer_origin;
		t_owned_ptr< t_abstract_bitmap16 >	m_buffer_ptr;
		t_scaled_adventure_map_drawer		m_draw_map;
		t_icon_info_list					m_icon_info_list;
		t_adventure_map const &				m_map;
		t_move_viewport_handler				m_move_viewport_handler;
		t_screen_point						m_viewport_pos;
		t_zoom_level						m_zoom_level;

		bool								m_dragging;
		t_screen_point						m_drag_start;

		// Member functions
		void	create_icons();
		void	drag( t_screen_point const & point );
		void	draw_map_to_buffer( t_screen_rect const & clip_rect );
		void	move_icons( t_screen_point const & delta );
		void	place_icon( t_window & icon_window, t_screen_point point );
	};

	// --------------------------------------------------------------------------
	// t_map_pane members
	// --------------------------------------------------------------------------

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_map_pane::t_zoom_level_properties const
	t_map_pane::k_zoom_level_properties_array[ k_zoom_level_count ] =
	{
		{ t_scaled_adventure_map_drawer::k_scale_half },
		{ t_scaled_adventure_map_drawer::k_scale_quarter },
		{ t_scaled_adventure_map_drawer::k_scale_eighth },
	};

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_screen_point t_map_pane::get_viewport_size(
		t_screen_point const &	window_size,
		t_zoom_level			zoom_level )
	{
		assert( zoom_level >= 0 && zoom_level < k_zoom_level_count );
		return unscale( window_size, k_zoom_level_properties_array[ zoom_level ].scale );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_map_pane::t_map_pane(
		t_screen_rect const &	rect,
		t_window *				parent_ptr,
		t_adventure_map const &	map,
		t_screen_point const &	viewport_pos,
		int						view_level,
		int						team_view,
		t_move_viewport_handler	move_viewport_handler )
		:	t_window( k_no_transparency, parent_ptr ),
			m_buffer_origin( 0, 0 ),
			m_buffer_ptr( new t_memory_bitmap16( rect.width(), rect.height() ) ),
			m_draw_map(	map, view_level, team_view ),
			m_map( map ),
			m_move_viewport_handler( move_viewport_handler ),
			m_viewport_pos( viewport_pos ),
			m_zoom_level( k_zoom_most ),
			m_dragging( false )
	{
		draw_map_to_buffer(
			t_screen_rect(
				t_screen_point( 0, 0 ),
				t_screen_point( rect.width(), rect.height() ) ) );

		create_icons();

		init( rect );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_adventure_map const & t_map_pane::get_adventure_map() const
	{
		return m_map;
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_screen_rect t_map_pane::get_map_view_rect() const
	{
		return get_view_rect( m_map );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	int t_map_pane::get_view_level() const
	{
		return m_draw_map.get_view_level();
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_screen_point t_map_pane::get_viewport_pos() const
	{
		return m_viewport_pos;
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_screen_point t_map_pane::get_viewport_size() const
	{
		return get_viewport_size( m_zoom_level );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_screen_point t_map_pane::get_viewport_size(
		t_zoom_level	zoom_level ) const
	{
		return	get_viewport_size(
					t_screen_point( m_buffer_ptr->get_width(), m_buffer_ptr->get_height() ),
					zoom_level );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_map_pane::t_zoom_level t_map_pane::get_zoom_level() const
	{
		return m_zoom_level;
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_map_pane::left_button_down( t_mouse_event const & event )
	{
		if ( capture_mouse() )
		{
			m_dragging = true;
			m_drag_start = event.screen_point;
		}
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_map_pane::left_button_up( t_mouse_event const & event )
	{
		if ( m_dragging )
		{
			drag( event.screen_point );
			release_mouse();
			m_dragging = false;
		}
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_map_pane::mouse_move( t_mouse_event const & event )
	{
		if ( !m_dragging )
			return;

		if ( event.left_button )
		{
			if ( get_mouse_position( this ) == event.client_point )
				drag( event.screen_point );
		}
		else
		{
			release_mouse();
			m_dragging = false;
		}
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_map_pane::move_viewport( t_screen_point const & new_viewport_pos )
	{
		if ( new_viewport_pos == m_viewport_pos )
			return;

		t_screen_point old_scaled_pos = scale( m_viewport_pos, k_zoom_level_properties_array[ m_zoom_level ].scale );
		t_screen_point new_scaled_pos = scale( new_viewport_pos, k_zoom_level_properties_array[ m_zoom_level ].scale );

		m_viewport_pos = new_viewport_pos;

		if ( new_scaled_pos == old_scaled_pos )
			return;

		t_screen_point viewport_delta = new_scaled_pos - old_scaled_pos;
		t_screen_point viewport_size( m_buffer_ptr->get_width(), m_buffer_ptr->get_height() );

		if (	viewport_delta.x >= viewport_size.x
			||	-viewport_delta.x >= viewport_size.x
			||	viewport_delta.y >= viewport_size.y
			||	-viewport_delta.y >= viewport_size.y )
		{
			m_buffer_origin = t_screen_point( 0, 0 );
			draw_map_to_buffer( t_screen_rect( t_screen_point( 0, 0 ), viewport_size ) );
		}
		else
		{
			m_buffer_origin += viewport_delta;

			if ( m_buffer_origin.x < 0 )
				m_buffer_origin.x += viewport_size.x;
			else if ( m_buffer_origin.x >= viewport_size.x )
				m_buffer_origin.x -= viewport_size.x;

			if ( m_buffer_origin.y < 0 )
				m_buffer_origin.y += viewport_size.y;
			else if ( m_buffer_origin.y >= viewport_size.y )
				m_buffer_origin.y -= viewport_size.y;

			t_screen_rect horiz_uncovered_rect;
			horiz_uncovered_rect.top = 0;
			horiz_uncovered_rect.bottom = viewport_size.y;

			t_screen_rect vert_uncovered_rect;
			vert_uncovered_rect.left = 0;
			vert_uncovered_rect.right = viewport_size.x;

			if ( viewport_delta.x >= 0 )
			{
				horiz_uncovered_rect.left = viewport_size.x - viewport_delta.x;
				horiz_uncovered_rect.right = viewport_size.x;
				vert_uncovered_rect.right = horiz_uncovered_rect.left;
			}
			else
			{
				horiz_uncovered_rect.left = 0;
				horiz_uncovered_rect.right = -viewport_delta.x;
				vert_uncovered_rect.left = horiz_uncovered_rect.right;
			}

			if ( viewport_delta.y >= 0 )
			{
				vert_uncovered_rect.top = viewport_size.y - viewport_delta.y;
				vert_uncovered_rect.bottom = viewport_size.y;
			}
			else
			{
				vert_uncovered_rect.top = 0;
				vert_uncovered_rect.bottom = -viewport_delta.y;
			}

			if ( horiz_uncovered_rect.width() > 0 )
				draw_map_to_buffer( horiz_uncovered_rect );

			if ( vert_uncovered_rect.height() > 0 )
				draw_map_to_buffer( vert_uncovered_rect );
		}

		invalidate();

		move_icons( -viewport_delta );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_map_pane::paint( t_paint_surface & paint_surface )
	{
		t_abstract_bitmap16_ptr dest_ptr = paint_surface.get_bitmap();
		t_screen_rect clip_rect = paint_surface.get_rect();

		t_screen_point viewport_size( m_buffer_ptr->get_width(), m_buffer_ptr->get_height() );
		assert( clip_rect.right <= viewport_size.x );
		assert( clip_rect.bottom <= viewport_size.y );

		// Draw upper left quadrant if clip rect intersects
		if (	clip_rect.left + m_buffer_origin.x < viewport_size.x
			&&	clip_rect.top + m_buffer_origin.y < viewport_size.y )
		{
			t_screen_rect source_rect = clip_rect + m_buffer_origin;
			if ( source_rect.right > viewport_size.x )
				source_rect.right = viewport_size.x;
			if ( source_rect.bottom > viewport_size.y )
				source_rect.bottom = viewport_size.y;

			draw( *dest_ptr, t_screen_point( 0, 0 ), *m_buffer_ptr, source_rect );
		}

		// Draw upper right quadrant if clip rect intersects
		if (	clip_rect.right + m_buffer_origin.x > viewport_size.x
			&&	clip_rect.top + m_buffer_origin.y < viewport_size.y )
		{
			int dest_x = 0;
			t_screen_rect source_rect = clip_rect + t_screen_point( m_buffer_origin.x - viewport_size.x, m_buffer_origin.y );
			if ( source_rect.left < 0 )
			{
				dest_x -= source_rect.left;
				source_rect.left = 0;
			}
			if ( source_rect.bottom > viewport_size.y )
				source_rect.bottom = viewport_size.y;

			draw( *dest_ptr, t_screen_point( dest_x, 0 ), *m_buffer_ptr, source_rect );
		}

		// Draw lower left quadrant if clip rect intersects
		if (	clip_rect.left + m_buffer_origin.x < viewport_size.x
			&&	clip_rect.bottom + m_buffer_origin.y > viewport_size.y )
		{
			int dest_y = 0;
			t_screen_rect source_rect = clip_rect + t_screen_point( m_buffer_origin.x, m_buffer_origin.y - viewport_size.y );
			if ( source_rect.right > viewport_size.x )
				source_rect.right = viewport_size.x;
			if ( source_rect.top < 0 )
			{
				dest_y -= source_rect.top;
				source_rect.top = 0;
			}

			draw( *dest_ptr, t_screen_point( 0, dest_y ), *m_buffer_ptr, source_rect );
		}

		// Draw lower right quadrant if clip rect intersects
		if (	clip_rect.right + m_buffer_origin.x > viewport_size.x
			&&	clip_rect.bottom + m_buffer_origin.y > viewport_size.y )
		{
			t_screen_point dest_pos( 0, 0 );
			t_screen_rect source_rect = clip_rect + m_buffer_origin - viewport_size;
			if ( source_rect.left < 0 )
			{
				dest_pos.x -= source_rect.left;
				source_rect.left = 0;
			}
			if ( source_rect.top < 0 )
			{
				dest_pos.y -= source_rect.top;
				source_rect.top = 0;
			}

			draw( *dest_ptr, dest_pos, *m_buffer_ptr, source_rect );
		}
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_map_pane::set_view_level( int new_view_level )
	{
		if ( new_view_level == m_draw_map.get_view_level() )
			return;

		m_draw_map.set_view_level( new_view_level );
		m_buffer_origin = t_screen_point( 0, 0 );

		draw_map_to_buffer(
			t_screen_rect(
				t_screen_point( 0, 0 ), 
				t_screen_point( m_buffer_ptr->get_width(), m_buffer_ptr->get_height() ) ) );

		invalidate();

		t_icon_info_list::const_iterator icon_info_end = m_icon_info_list.end();
		t_icon_info_list::const_iterator icon_info_iter = m_icon_info_list.begin();
		for ( ; icon_info_iter != icon_info_end; ++icon_info_iter )
			icon_info_iter->window_ptr->set_visible( icon_info_iter->level == new_view_level );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_map_pane::set_zoom_level(
		t_zoom_level			new_zoom_level,
		t_screen_point const &	new_viewport_pos )
	{
		assert( new_zoom_level >= 0 && new_zoom_level < k_zoom_level_count );

		if ( new_zoom_level == m_zoom_level && new_viewport_pos == m_viewport_pos )
			return;

		m_zoom_level = new_zoom_level;
		m_viewport_pos = new_viewport_pos;
		m_buffer_origin = t_screen_point( 0, 0 );

		draw_map_to_buffer(
			t_screen_rect(
				t_screen_point( 0, 0 ), 
				t_screen_point( m_buffer_ptr->get_width(), m_buffer_ptr->get_height() ) ) );

		invalidate();

		t_icon_info_list::const_iterator icon_info_end = m_icon_info_list.end();
		t_icon_info_list::const_iterator icon_info_iter = m_icon_info_list.begin();
		for ( ; icon_info_iter != icon_info_end; ++icon_info_iter )
			place_icon( *icon_info_iter->window_ptr, icon_info_iter->screen_point );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_map_pane::create_icons()
	{
		int view_level = m_draw_map.get_view_level();
		int team_view = m_draw_map.get_team_view();

		// Build a list of ids of all of the objects on the map which have an
		// icon type
		typedef std::vector< t_adventure_map::t_object_id > t_object_id_list;
		t_object_id_list object_ids;

		t_adventure_map::t_object_id object_id;
		for (	object_id = m_map.get_first_object_id();
				object_id != t_adventure_map::k_invalid_object_id;
				object_id = m_map.get_next_object_id( object_id ) )
		{
			t_adventure_object const & object = m_map.get_adv_object( object_id );
			if (	object.get_icon_type() != k_adv_object_icon_none
				&&	!object.hidden_by_shroud( team_view )
				&&	!object.hidden_by_fog_of_war( team_view ) )
				object_ids.push_back( object_id );
		}

		// Sort the ids back to front
		std::sort( object_ids.begin(), object_ids.end(), t_compare_depth( m_map ) );

		// Build the icon info for each object with an icon
		m_icon_info_list.resize( object_ids.size() );

		t_icon_info_list::iterator icon_info_iter = m_icon_info_list.begin();
		t_object_id_list::const_iterator object_id_end = object_ids.end();
		t_object_id_list::const_iterator object_id_iter = object_ids.begin();
		for ( ; object_id_iter != object_id_end; ++object_id_iter, ++icon_info_iter )
		{
			t_icon_info & icon_info = *icon_info_iter;

			t_adventure_object const & object = m_map.get_adv_object( *object_id_iter );
			assert( object.get_icon_type() != k_adv_object_icon_none );
			t_level_map_point_2d const & object_pos = object.get_position();
			
			icon_info.level = object_pos.level;

			// Get the screen point of the objects base
			icon_info.screen_point = m_map.get_screen_point( object_pos );
			icon_info.screen_point.y -= m_map.get_object_height( *object_id_iter );

			// Adjust the screen point to the center of its footprint
			t_map_point_2d const & footprint_size = object.get_footprint().get_size();
			icon_info.screen_point.x += ( footprint_size.column - footprint_size.row ) * k_adventure_tile_width / 4;
			icon_info.screen_point.y += ( footprint_size.column + footprint_size.row ) * k_adventure_tile_height / 4;

			// Get the owner's color
			t_player_color owner_color;
			int owner_number = object.get_owner_number();
			if ( owner_number >= 0 )
				owner_color = m_map.get_player( owner_number ).get_color();
			else
				owner_color = k_player_gray;

			// Create the icon window
			icon_info.window_ptr = create_icon_window( t_screen_point(), this, object.get_icon_type(), owner_color );
			icon_info.window_ptr->set_help_balloon_text( object.get_balloon_help() );
			place_icon( *icon_info.window_ptr, icon_info.screen_point );
			icon_info.window_ptr->set_visible( icon_info.level == view_level );
		}
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_map_pane::drag( t_screen_point const & point )
	{
		t_scaled_adventure_map_drawer::t_scale scale_id =
			k_zoom_level_properties_array[ m_zoom_level ].scale;

		t_screen_point delta = m_drag_start - point;
		t_screen_point new_viewport_pos = unscale( scale( m_viewport_pos, scale_id ) + delta, scale_id );
		m_drag_start = point;
		m_move_viewport_handler( new_viewport_pos );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_map_pane::draw_map_to_buffer( t_screen_rect const & clip_rect )
	{
		t_scaled_adventure_map_drawer::t_scale scale_id =
			k_zoom_level_properties_array[ m_zoom_level ].scale;

		t_screen_point scaled_pos = scale( m_viewport_pos, scale_id );
		t_screen_point scaled_size( m_buffer_ptr->get_width(), m_buffer_ptr->get_height() );
		assert( clip_rect.right <= scaled_size.x );
		assert( clip_rect.bottom <= scaled_size.y );

		// Draw upper left quadrant if clip_rect intersects
		if (	clip_rect.left + m_buffer_origin.x < scaled_size.x
			&&	clip_rect.top + m_buffer_origin.y < scaled_size.y )
		{
			t_screen_rect source_rect = clip_rect + scaled_pos;
			t_screen_point dest_pos = clip_rect.top_left() + m_buffer_origin;
			if ( dest_pos.x + source_rect.width() > scaled_size.x )
				source_rect.right = source_rect.left + scaled_size.x - dest_pos.x;
			if ( dest_pos.y + source_rect.height() > scaled_size.y )
				source_rect.bottom = source_rect.top + scaled_size.y - dest_pos.y;

			m_draw_map( scale_id, source_rect, *m_buffer_ptr, dest_pos );
		}

		// Draw upper right quadrant if clip_rect intersects
		if (	clip_rect.right + m_buffer_origin.x > scaled_size.x
			&&	clip_rect.top + m_buffer_origin.y < scaled_size.y )
		{
			t_screen_rect source_rect = clip_rect + scaled_pos;
			t_screen_point dest_pos = clip_rect.top_left() + t_screen_point( m_buffer_origin.x - scaled_size.x, m_buffer_origin.y );
			if ( dest_pos.x < 0 )
			{
				source_rect.left -= dest_pos.x;
				dest_pos.x = 0;
			}
			if ( dest_pos.y + source_rect.height() > scaled_size.y )
				source_rect.bottom = source_rect.top + scaled_size.y - dest_pos.y;

			m_draw_map( scale_id, source_rect, *m_buffer_ptr, dest_pos );
		}

		// Draw lower left quadrant if clip_rect intersects
		if (	clip_rect.left + m_buffer_origin.x < scaled_size.x
			&&	clip_rect.bottom + m_buffer_origin.y > scaled_size.y )
		{
			t_screen_rect source_rect = clip_rect + scaled_pos;
			t_screen_point dest_pos = clip_rect.top_left() + t_screen_point( m_buffer_origin.x, m_buffer_origin.y - scaled_size.y );
			if ( dest_pos.x + source_rect.width() > scaled_size.x )
				source_rect.right = source_rect.left + scaled_size.x - dest_pos.x;
			if ( dest_pos.y < 0 )
			{
				source_rect.top -= dest_pos.y;
				dest_pos.y = 0;
			}

			m_draw_map( scale_id, source_rect, *m_buffer_ptr, dest_pos );
		}

		// Draw lower right quadrant if clip_rect intersects
		if (	clip_rect.right + m_buffer_origin.x > scaled_size.x
			&&	clip_rect.bottom + m_buffer_origin.y > scaled_size.y )
		{
			t_screen_rect source_rect = clip_rect + scaled_pos;
			t_screen_point dest_pos = clip_rect.top_left() + m_buffer_origin - scaled_size;
			if ( dest_pos.x < 0 )
			{
				source_rect.left -= dest_pos.x;
				dest_pos.x = 0;
			}
			if ( dest_pos.y < 0 )
			{
				source_rect.top -= dest_pos.y;
				dest_pos.y = 0;
			}

			m_draw_map( scale_id, source_rect, *m_buffer_ptr, dest_pos );
		}
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_map_pane::move_icons( t_screen_point const & delta )
	{
		t_icon_info_list::const_iterator icon_info_end = m_icon_info_list.end();
		t_icon_info_list::const_iterator icon_info_iter = m_icon_info_list.begin();
		for ( ; icon_info_iter != icon_info_end; ++icon_info_iter )
		{
			t_window & icon_window = *icon_info_iter->window_ptr;
			icon_window.move( icon_window.to_parent( icon_window.get_screen_rect() ) + delta );
		}
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_map_pane::place_icon(
		t_window &		icon_window,
		t_screen_point 	point )
	{
		point -= m_viewport_pos;
		point = scale( point, k_zoom_level_properties_array[ m_zoom_level ].scale );

		t_screen_point icon_size = icon_window.get_screen_rect().size();
		point.x -= icon_size.x / 2;
		point.y -= icon_size.y;

		icon_window.move( t_screen_rect( point, icon_size ) );
	}

	// --------------------------------------------------------------------------
	// t_mini_map_pane class
	// --------------------------------------------------------------------------

	class t_mini_map_pane : public t_window, private t_mini_map_renderer::t_client
	{
	public:
		// Types
		typedef t_handler_1< t_screen_point const & > t_move_viewport_handler;

		// Constructor
		t_mini_map_pane(
			t_screen_rect const &	rect,
			t_window *				parent_ptr,
			t_adventure_map const &	map,
			int						view_level,
			int						team_view,
			t_move_viewport_handler	move_viewport_handler );

		// Member functions
		int				get_view_level() const;
		virtual void	left_button_down( t_mouse_event const & event );
		virtual void	left_button_up( t_mouse_event const & event );
		virtual void	mouse_move( t_mouse_event const & event );
		void			move_viewport( t_screen_point const & new_viewport_pos );
		void			move_viewport( t_screen_rect const & new_viewport_rect );
		virtual void	paint( t_paint_surface & paint_surface );
		void			set_view_level( int new_view_level );

	private:
		// Data members
		t_shared_ptr< t_memory_bitmap16 >		m_back_buffer_ptr;
		bool								m_dragging;
		t_move_viewport_handler				m_move_viewport_handler;
		t_owned_ptr< t_mini_map_renderer >	m_renderer_ptr;

		// Member functions
		virtual t_back_buffer_ptr	create_back_buffer( t_screen_point const & size );
		void						drag( t_screen_point const & point );
		virtual void				on_rect_dirtied( t_screen_rect const & dirted_rect );
	};

	// --------------------------------------------------------------------------
	// t_mini_map_pane members
	// --------------------------------------------------------------------------

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
#pragma warning( push )
#pragma warning( disable: 4355 )
	t_mini_map_pane::t_mini_map_pane(
		t_screen_rect const &	rect,
		t_window *				parent_ptr,
		t_adventure_map const &	map,
		int						view_level,
		int						team_view,
		t_move_viewport_handler	move_viewport_handler )
		:	t_window( rect, k_no_transparency, parent_ptr ),
			m_back_buffer_ptr( new t_memory_bitmap16(rect.width(), rect.height()) ),
			m_dragging( false ),
			m_move_viewport_handler( move_viewport_handler ),
			m_renderer_ptr( new t_mini_map_renderer( *this, map, team_view ) )
	{
		m_renderer_ptr->set_view_level( view_level );
		m_renderer_ptr->resize( rect.size() );
	}
#pragma warning( pop )

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	int t_mini_map_pane::get_view_level() const
	{
		return m_renderer_ptr->get_view_level();
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_mini_map_pane::left_button_down( t_mouse_event const & event )
	{
		if ( capture_mouse() )
		{
			m_dragging = true;
			drag( event.client_point );
		}
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_mini_map_pane::left_button_up( t_mouse_event const & event )
	{
		if ( m_dragging )
		{
			release_mouse();
			m_dragging = false;
		}
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_mini_map_pane::mouse_move( t_mouse_event const & event )
	{
		if ( !m_dragging )
			return;

		if ( event.left_button )
		{
			if ( get_mouse_position( this ) == event.client_point )
				drag( event.client_point );
		}
		else
		{
			release_mouse();
			m_dragging = false;
		}
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_mini_map_pane::move_viewport(
		t_screen_point const &	new_viewport_pos )
	{
		m_renderer_ptr->set_viewport_rect(
			t_screen_rect( new_viewport_pos, m_renderer_ptr->get_viewport_rect().size() ) );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_mini_map_pane::move_viewport(
		t_screen_rect const &	new_viewport_rect )
	{
		m_renderer_ptr->set_viewport_rect( new_viewport_rect );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_mini_map_pane::paint( t_paint_surface & paint_surface )
	{
		m_renderer_ptr->update( paint_surface.get_rect() );
		paint_surface.draw( t_screen_point( 0, 0 ), *m_back_buffer_ptr, m_back_buffer_ptr->get_rect() );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_mini_map_pane::set_view_level( int new_view_level )
	{
		m_renderer_ptr->set_view_level( new_view_level );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_mini_map_pane::t_back_buffer_ptr
	t_mini_map_pane::create_back_buffer( t_screen_point const & size )
	{
		assert( m_back_buffer_ptr->get_width() >= size.x );
		assert( m_back_buffer_ptr->get_height() >= size.y );
		return m_back_buffer_ptr;
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_mini_map_pane::drag( t_screen_point const & point )
	{
		m_move_viewport_handler(
			m_renderer_ptr->mini_to_view( point ) - m_renderer_ptr->get_viewport_rect().size() / 2 );
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void t_mini_map_pane::on_rect_dirtied( t_screen_rect const & dirtied_rect )
	{
		invalidate( dirtied_rect );
	}

	// --------------------------------------------------------------------------
	// Non-member functions
	// --------------------------------------------------------------------------

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	char const * get_text_layout_name( t_adv_object_icon_type icon_type )
	{
		assert( icon_type >= 0 && icon_type < k_adv_object_icon_type_count );

		static char const * const k_layer_names[ k_adv_object_icon_type_count ] =
		{
			"alchemist's_lab",
			"army",
			"artifact",
			"dwelling",
			"crystal",
			"crystal_mine",
			"gem_mine",
			"gems",
			"gold",
			"gold_mine",
			"mercury",
			"ore",
			"ore_mine",
			"sawmill",
			"gate",
			"sulfur",
			"sulfur_mine",
			"teleporter",
			"town",
			"wood",
		};

		return k_layer_names[ icon_type ];
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	std::string const & get_text( t_adv_object_icon_type icon_type )
	{
		assert( icon_type >= 0 && icon_type < k_adv_object_icon_type_count );

		static std::string text_array[ k_adv_object_icon_type_count ];
		static bool initialized = false;
		if ( !initialized )
		{
			t_adv_object_icon_type type;
			for ( type = t_adv_object_icon_type( 0 ); type < k_adv_object_icon_type_count; enum_incr( type ) )
				text_array[ type ] = t_external_string( std::string( get_text_layout_name( type ) ) + ".view_world" );

			initialized = true;
		}

		return text_array[ icon_type ];
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	char const * get_icon_layout_name( t_adv_object_icon_type icon_type )
	{
		assert( icon_type >= 0 && icon_type < k_adv_object_icon_type_count );

		static char const * const k_layer_names[ k_adv_object_icon_type_count ] =
		{
			"alchemist's_lab",
			"armies",
			"artifacts",
			"dwellings",
			"crystal",
			"crystal_mine",
			"gem_mine",
			"gems",
			"gold",
			"gold_mine",
			"mercury",
			"ore",
			"ore_mine",
			"sawmill",
			"gates",
			"sulfur",
			"sulfur_mine",
			"teleporters",
			"towns",
			"wood",
		};

		return k_layer_names[ icon_type ];
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void clamp_map_viewport_pos(
		t_screen_point &		viewport_pos,
		t_screen_point const &	viewport_size,
		t_screen_rect const &	map_view_rect )
	{
		t_screen_point map_view_size = map_view_rect.size();

		if ( viewport_size.x < map_view_size.x )
		{
			if ( viewport_pos.x < map_view_rect.left )
				viewport_pos.x = map_view_rect.left;
			else if ( viewport_pos.x + viewport_size.x > map_view_rect.right )
				viewport_pos.x = map_view_rect.right - viewport_size.x;
		}
		else
			viewport_pos.x = map_view_rect.left + ( map_view_size.x - viewport_size.x ) / 2;

		if ( viewport_size.y < map_view_size.y )
		{
			if ( viewport_pos.y < map_view_rect.top )
				viewport_pos.y = map_view_rect.top;
			else if ( viewport_pos.y + viewport_size.y > map_view_rect.bottom )
				viewport_pos.y = map_view_rect.bottom - viewport_size.y;
		}
		else
			viewport_pos.y = map_view_rect.top + ( map_view_size.y - viewport_size.y ) / 2;
	}

} // Unnamed namespace

// --------------------------------------------------------------------------
// t_view_world_dialog::t_impl class
// --------------------------------------------------------------------------

class t_view_world_dialog::t_impl
{
public:
	// Data members
	t_bitmap_group_ptr					m_layout_ptr;
	t_counted_ptr< t_map_pane >			m_map_pane_ptr;
	t_counted_ptr< t_mini_map_pane >	m_mini_map_pane_ptr;
	t_view_world_dialog &				m_outer;
	t_button_ptr						m_surface_button_ptr;
	t_button_ptr						m_underground_button_ptr;
	t_button_group						m_zoom_button_group;
	t_bitmap_group_ptr					m_zoom_button_layers_ptr;

	// Constructor
	t_impl(
		t_view_world_dialog &	outer,
		t_adventure_map const &	map,
		t_screen_point const &	center_point,
		int						view_level,
		int						team_view );

	// Member functions
	void	clamp_viewport_pos(
				t_screen_point &			viewport_pos,
				t_map_pane::t_zoom_level	zoom_level ) const;
	void	create_close_button( t_help_block const & help );
	void	create_legend();
	void	create_legend_icon( t_adv_object_icon_type icon_type );
	void	create_legend_text( t_adv_object_icon_type icon_type );
	void	create_map_pane(
				t_adventure_map const &	map,
				t_screen_point const &	center_point,
				int						view_level,
				int						team_view );
	void	create_mini_map_pane(
				t_adventure_map const &	map,
				int						view_level,
				int						team_view );
	void	create_view_level_buttons(
				bool					two_levels,
				t_help_block const &	help_block );
	void	create_zoom_button(
				std::string const &			name,
				t_map_pane::t_zoom_level	zoom_level,
				t_help_block const &		help_block );
	void	move_viewport( t_screen_point const & new_viewport_pos );
	void	ok_clicked( t_button * button_ptr );
	void	view_level_clicked( t_button * button_ptr );
	void	zoom_clicked(
				t_button *					button_ptr,
				t_map_pane::t_zoom_level	zoom_level );
};

// --------------------------------------------------------------------------
// t_view_world_dialog::t_impl members
// --------------------------------------------------------------------------

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_view_world_dialog::t_impl::t_impl(
	t_view_world_dialog &	outer,
	t_adventure_map const &	map,
	t_screen_point const &	center_point,
	int						view_level,
	int						team_view )
	:	m_layout_ptr( g_layout_cache.get() ),
		m_outer( outer ),
		m_zoom_button_layers_ptr( g_zoom_button_cache.get() )
{
	t_bitmap_layer const * background_ptr = m_layout_ptr->find( "background" );
	m_outer.set_bitmap( background_ptr );

	t_help_block const & help = get_help_block( "view_world" );

	create_close_button( help );

	create_map_pane( map, center_point, view_level, team_view );
	create_mini_map_pane( map, view_level, team_view );

	create_zoom_button( "1", t_map_pane::k_zoom_least, help );
	create_zoom_button( "2", t_map_pane::k_zoom_medium, help );
	create_zoom_button( "4", t_map_pane::k_zoom_most, help );
	m_zoom_button_group.select( 2 );

	create_view_level_buttons( map.get_num_levels() > 1, help );

	create_legend();

	t_screen_rect layout_rect = m_layout_ptr->get_rect();
	t_screen_rect parent_rect = m_outer.get_parent()->get_client_rect();
	m_outer.move( t_screen_rect( ( parent_rect.size() - layout_rect.size() ) / 2, layout_rect.size() ) );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_view_world_dialog::t_impl::clamp_viewport_pos(
	t_screen_point &			viewport_pos,
	t_map_pane::t_zoom_level	zoom_level ) const
{
	t_screen_rect map_view_rect = m_map_pane_ptr->get_map_view_rect();
	t_screen_point viewport_size = m_map_pane_ptr->get_viewport_size( zoom_level );
	clamp_map_viewport_pos( viewport_pos, viewport_size, map_view_rect );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_view_world_dialog::t_impl::create_close_button(
	t_help_block const &	help )
{
	assert( m_layout_ptr->find( "close_button" ) != 0 );
	t_screen_rect rect = m_layout_ptr->find( "close_button" )->get_rect();
	t_button_ptr close_button_ptr = new t_button( g_ok_button.get(), rect.top_left(), &m_outer );
	close_button_ptr->set_click_handler( bound_handler( *this, &t_impl::ok_clicked ) );
	set_help( close_button_ptr, help, "ok" );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_view_world_dialog::t_impl::create_legend()
{
	t_adv_object_icon_type icon_type;
	for (	icon_type = t_adv_object_icon_type( 0 );
			icon_type < k_adv_object_icon_type_count;
			enum_incr( icon_type ) )
	{
		create_legend_icon( icon_type );
		create_legend_text( icon_type );
	}
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_view_world_dialog::t_impl::create_legend_icon(
	t_adv_object_icon_type	icon_type )
{
	char const * layout_name = get_icon_layout_name( icon_type );
	assert( m_layout_ptr->find( layout_name ) != 0 );
	t_screen_rect rect = m_layout_ptr->find( layout_name )->get_rect();
	create_icon_window( rect.top_left(), &m_outer, icon_type );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_view_world_dialog::t_impl::create_legend_text(
	t_adv_object_icon_type	icon_type )
{
	std::string const & layout_name = std::string( get_text_layout_name( icon_type ) ) + "_text";
	assert( m_layout_ptr->find( layout_name ) != 0 );
	t_screen_rect rect = m_layout_ptr->find( layout_name )->get_rect();
	t_text_window_ptr text_window_ptr =
		new t_text_window( get_font( rect.height() / 2 ), rect, &m_outer, get_text( icon_type ), t_pixel_24( 0, 0, 0 ) );
	text_window_ptr->set_center_vertical();
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_view_world_dialog::t_impl::create_map_pane(
	t_adventure_map const &	map,
	t_screen_point const &	center_point,
	int						view_level,
	int						team_view )
{
	assert( m_layout_ptr->find( "adventure_window" ) != 0 );
	t_screen_rect rect = m_layout_ptr->find( "adventure_window" )->get_rect();

	t_screen_point viewport_size =
		t_map_pane::get_viewport_size( rect.size(), t_map_pane::k_zoom_most );
	t_screen_point viewport_pos = center_point - viewport_size / 2;
	clamp_map_viewport_pos( viewport_pos, viewport_size, get_view_rect( map ) );

	m_map_pane_ptr =
		new t_map_pane( rect, &m_outer, map, viewport_pos, view_level, team_view, bound_handler( *this, &t_impl::move_viewport ) );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_view_world_dialog::t_impl::create_mini_map_pane(
	t_adventure_map const &	map,
	int						view_level,
	int						team_view )
{
	assert( m_layout_ptr->find( "mini_map" ) != 0 );
	t_screen_rect rect = m_layout_ptr->find( "mini_map" )->get_rect();
	m_mini_map_pane_ptr =
		new t_mini_map_pane( rect, &m_outer, map, view_level, team_view, bound_handler( *this, &t_impl::move_viewport ) );
	m_mini_map_pane_ptr->move_viewport(
		t_screen_rect( m_map_pane_ptr->get_viewport_pos(), m_map_pane_ptr->get_viewport_size() ) );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_view_world_dialog::t_impl::create_view_level_buttons(
	bool					two_levels,
	t_help_block const &	help )
{
	assert( m_layout_ptr->find( "underground" ) != 0 );

	t_screen_rect rect = m_layout_ptr->find( "underground" )->get_rect();

	m_underground_button_ptr = new t_button( g_underground_button.get(), rect.top_left(), &m_outer );
	m_underground_button_ptr->set_click_handler( bound_handler( *this, &t_impl::view_level_clicked ) );
	m_underground_button_ptr->set_visible( m_map_pane_ptr->get_view_level() <= 0 );
	set_help( m_underground_button_ptr, help, "underground" );

	if ( two_levels )
	{
		m_surface_button_ptr = new t_button( g_surface_button.get(), rect.top_left(), &m_outer );
		m_surface_button_ptr->set_click_handler( bound_handler( *this, &t_impl::view_level_clicked ) );
		m_surface_button_ptr->set_visible( m_map_pane_ptr->get_view_level() > 0 );
		set_help( m_surface_button_ptr, help, "surface" );
	}
	else
		m_underground_button_ptr->enable( false );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_view_world_dialog::t_impl::create_zoom_button(
	std::string const &			name,
	t_map_pane::t_zoom_level	zoom_level,
	t_help_block const &		help )
{
	std::string layout_name = "magnify_";
	layout_name += name;

	assert( m_layout_ptr->find( layout_name ) != 0 );
	t_screen_rect rect = m_layout_ptr->find( layout_name )->get_rect();

	t_toggle_button_ptr zoom_button_ptr = new t_toggle_button( rect.top_left(), &m_outer, true );
	set_button_layers( zoom_button_ptr, m_zoom_button_layers_ptr, name );
	zoom_button_ptr->set_click_handler(
		add_2nd_argument(
			bound_handler( *this, &t_impl::zoom_clicked ), 
			zoom_level ) );
	set_help( zoom_button_ptr, help, layout_name );

	m_zoom_button_group.add( zoom_button_ptr );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_view_world_dialog::t_impl::move_viewport(
	t_screen_point const &	new_viewport_pos )
{
	t_screen_point viewport_pos = new_viewport_pos;
	clamp_viewport_pos( viewport_pos, m_map_pane_ptr->get_zoom_level() );

	m_map_pane_ptr->move_viewport( viewport_pos );
	m_mini_map_pane_ptr->move_viewport( viewport_pos );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_view_world_dialog::t_impl::ok_clicked( t_button * button_ptr )
{
	m_outer.close();
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_view_world_dialog::t_impl::view_level_clicked( t_button * button_ptr )
{
	int new_view_level = m_map_pane_ptr->get_view_level() == 0 ? 1 : 0;

	m_surface_button_ptr->set_visible( new_view_level > 0 );
	m_underground_button_ptr->set_visible( new_view_level <= 0 );
	m_map_pane_ptr->set_view_level( new_view_level );
	m_mini_map_pane_ptr->set_view_level( new_view_level );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_view_world_dialog::t_impl::zoom_clicked(
	t_button *					button_ptr,
	t_map_pane::t_zoom_level	zoom_level )
{
	assert( zoom_level >= 0 && zoom_level < t_map_pane::k_zoom_level_count );

	if ( zoom_level == m_map_pane_ptr->get_zoom_level() )
		return;

	t_screen_point old_viewport_size = m_map_pane_ptr->get_viewport_size();
	t_screen_point new_viewport_size = m_map_pane_ptr->get_viewport_size( zoom_level );

	t_screen_point new_viewport_pos = m_map_pane_ptr->get_viewport_pos() + ( old_viewport_size - new_viewport_size ) / 2;
	clamp_viewport_pos( new_viewport_pos, zoom_level );

	m_map_pane_ptr->set_zoom_level( zoom_level, new_viewport_pos );
	m_mini_map_pane_ptr->move_viewport(
		t_screen_rect( m_map_pane_ptr->get_viewport_pos(), m_map_pane_ptr->get_viewport_size() ) );
}

// --------------------------------------------------------------------------
// t_view_world_dialog members
// --------------------------------------------------------------------------

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
#pragma warning( push )
#pragma warning( disable: 4355 )
t_view_world_dialog::t_view_world_dialog(
	t_window *				parent_ptr,
	t_adventure_map const &	map,
	t_screen_point const &	center_point,
	int						view_level,
	int						team_view )
	:	t_bitmap_layer_window( 0, t_screen_point( 0, 0 ), parent_ptr ),
		m_impl_ptr( new t_impl( *this, map, center_point, view_level, team_view ) )
{
}
#pragma warning( pop )

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_view_world_dialog::~t_view_world_dialog()
{
}
